﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Samples.WindowsPhoneCloud.StorageClient;
using Microsoft.Samples.Data.Services.Client;
using System.Collections.ObjectModel;
using Microsoft.Samples.WindowsPhoneCloud.StorageClient.Credentials;
using AzureTracer.Model;
using System.Collections.Generic;
using System.Threading;

namespace AzureTracer.Services
{
    public class AzureLogService : ILogService
    {
        private IAppSettings _appSetting;
        private TableServiceContext _context;
        private DataServiceCollection<LogEntity> _logTable;

        const string TABLE_URI_BASE = "https://{0}.table.core.windows.net";
        const string QUERY_TEMPLATE = "{0}WADLogsTable()?$filter=PartitionKey gt '{1}'";
        const string QUERYTEST_TEMPLATE = "{0}WADLogsTable()?$top=1";
        const string LOG_KEY = "Log";

        const string ERROR_STRING = "error";
        const string WARNING_STRING = "warning";
        const string INFO_STRING = "info";
       
        public ObservableCollection<LogEntity> LogList { get; set; }

        public AzureLogService(IAppSettings appSettings)
        {
            _appSetting = appSettings;
            _appSetting.PropertyChanged += AppSettingPropertyChanged;
            if (!string.IsNullOrEmpty(_appSetting.AccountName))
                BuildCredentials(_appSetting.AccountName, _appSetting.AccountKey);
        }

        void AppSettingPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (!string.IsNullOrEmpty(_appSetting.AccountName) && !(string.IsNullOrEmpty(_appSetting.AccountKey)))
                BuildCredentials(_appSetting.AccountName, _appSetting.AccountKey);
        }

        private void BuildCredentials(string accountName, string accountKey)
        {
            StorageCredentialsAccountAndKey credentials = new StorageCredentialsAccountAndKey
                (accountName, accountKey);
            _context = new TableServiceContext(string.Format(TABLE_URI_BASE, accountName), credentials);            
            _logTable = new DataServiceCollection<LogEntity>(_context);
        }

        private string MaxParitionKey
        {
            get
            {
                //contains the universal time (UTC) ticks of the event. 
                //The UTC time has the seconds portion removed and the ticks value is 
                //prepended with a "0" string value.
                var maxDate = DateTime.Now.AddDays(-_appSetting.MaxDaysSync);
                maxDate = maxDate.AddSeconds(-maxDate.Second);
                return "0" + maxDate.ToUniversalTime().Ticks.ToString();
            }
        }

        public void LoadData(Action<int, Exception> callback)
        {
            
            string partitionKey = (_appSetting.LastKeySync == string.Empty) ?
                MaxParitionKey : _appSetting.LastKeySync;

            Uri logUri = new Uri(
                string.Format(QUERY_TEMPLATE, _context.BaseUri, partitionKey),
                UriKind.Absolute);

            ClearOldLogs();
            _logTable.LoadCompleted += (sender, e) =>
                {
                    // Running on a background thread.
                    ThreadPool.QueueUserWorkItem((o) =>
                    {                       
                        if (e.Error != null)
                        {
                            callback(0, e.Error.InnerException); return;
                        }

                        // Consume a data feed that contains paged results.        
                        if (_logTable.Continuation != null)
                        {
                            // If there is a continuation token,             
                            // load the next page of results.           
                            _logTable.LoadNextPartialSetAsync();
                        }
                        else
                        {
                            int newLogCount = 0;
                        
                            foreach (LogEntity item in _logTable)
                                if (!LogList.Contains(item))
                                {
                                    CheckAndFixLevel(item);
                                    // Running on the UI thread.   
                                    Deployment.Current.Dispatcher.BeginInvoke(
                                        (Action<LogEntity>)((log) => LogList.Add(log)), item);
                                    newLogCount++;
                                }

                            if (_logTable.Count != 0)
                            {
                                _appSetting.LastKeySync = _logTable[_logTable.Count - 1].PartitionKey;
                                _appSetting.AnySyncEnding = true;
                            }
                            _appSetting.LastTimeSync = DateTime.Now;
                            _logTable.Clear();
                            callback(newLogCount, null);
                        }
                    });
                };

            _logTable.LoadAsync(logUri);
        }

        private void CheckAndFixLevel(LogEntity item)
        {
            if (item.Level == 5)
            {
                string messageLower = item.Message.ToLowerInvariant();
                if (messageLower.Contains(INFO_STRING))
                    item.Level = 4;
                else if (messageLower.Contains(WARNING_STRING))
                    item.Level = 3;
                else if (messageLower.Contains(ERROR_STRING))
                    item.Level = 2;
            }
        }

        private void ClearOldLogs()
        {
            DateTime maxDate = DateTime.Now.AddDays(-_appSetting.MaxDaysSync);
            for (int i = 0; i < LogList.Count; i++)
                if (LogList[i].Timestamp < maxDate)
                {
                    LogList.Remove(LogList[i]);
                    i--;
                }
        }

        public void CheckService(string accountName, string accountKey, Action<Exception> callback)
        {
            BuildCredentials(accountName, accountKey);
            Uri logUri = new Uri(
              string.Format(QUERYTEST_TEMPLATE, _context.BaseUri),
              UriKind.Absolute);

            _logTable.LoadCompleted += (sender, e) =>
                {
                    if (e.Error != null)
                        callback(e.Error.InnerException);
                    else
                    {
                        _appSetting.AnySyncEnding = _logTable.Count != 0;

                        _logTable.Clear();
                        callback(null);
                    }
                };
            _logTable.LoadAsync(logUri);
        }
    }
}